package llama

import (
	"Lunar-Astral-API/local_server/config" // 导入项目配置模块（如路径、端口等）
	"Lunar-Astral-API/local_server/utils"  // 导入工具函数（如打开浏览器）
	"log"                                  // 标准日志包，用于输出调试/错误信息
	"os"                                   // 提供操作系统相关功能（如文件操作）
)

/**
 * @description: 初始化配置，获取模型路径，并启动不同类型的 GGUF 服务实例
 * @description: 若配置初始化失败或未找到任何有效模型
 * @description: 输出相应日志并可能打开模型下载页面
 * @return {*}
 */
func CreateServers() {
	// 初始化配置，若初始化失败则直接返回
	if !initConfig() {
		return
	}
	// 获取不同类型模型的路径，优先使用直接指定的模型路径，若未指定则使用默认子目录下的第一个模型
	// 模型类型包括：嵌入模型、推理模型和视觉模型
	modelPaths := map[string]string{
		"visualing": getSpecifiedOrFirstModel("visualing", *config.ModelVisualingIndex, *config.ModelFilePath+"/visualing"),
		"reasoning": getSpecifiedOrFirstModel("reasoning", *config.ModelReasoningIndex, *config.ModelFilePath+"/reasoning"),
		"embedding": getSpecifiedOrFirstModel("embedding", *config.ModelEmbeddingIndex, *config.ModelFilePath+"/embedding"),
	}
	// 初始化标志位，用于判断是否所有模型路径都为空
	allEmpty := true
	// 遍历所有模型路径，检查是否存在非空路径
	// 若存在非空路径，则更新标志位并跳出循环
	for _, path := range modelPaths {
		if path != "" {
			allEmpty = false
			break
		}
	}
	// 若所有模型路径都为空，输出错误日志提示未找到有效模型文件
	// 同时启动浏览器打开模型下载页面，方便用户下载模型，最后返回
	if allEmpty {
		log.Printf("GGUF服务[ERROR] -> 所有类型均未找到有效模型文件")
		// 启动浏览器页面，辅助用户下载模型
		utils.OpenBrowser("https://modelscope.cn/models/unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF/files")
		utils.OpenBrowser("https://modelscope.cn/models/Qwen/Qwen3-Embedding-0.6B-GGUF/files")
		utils.OpenBrowser("https://modelscope.cn/models/OpenBMB/MiniCPM-V-4_5-gguf/files")
		return
	}
	// 使用获取到的模型路径启动不同类型的 GGUF 服务实例
	startServersWithTypes(modelPaths)
}

/**
 * @description: 初始化 GGUF 服务所需的配置，包括检查执行文件是否存在、设置执行权限，以及创建模型子目录
 * @return {bool} 初始化是否成功
 */
func initConfig() bool {
	// 从配置中获取 LLM 服务执行文件的路径
	llmServerPath := *config.ModelServerSource
	// 检查执行文件是否存在，若不存在则记录错误日志并返回 false
	if _, err := os.Stat(llmServerPath); os.IsNotExist(err) {
		log.Printf("GGUF服务[ERROR] -> 执行文件[%s]不存在", llmServerPath)
		return false
	}
	// 为执行文件设置 0755 权限，若设置失败则记录错误日志并返回 false
	if err := os.Chmod(llmServerPath, 0755); err != nil {
		log.Printf("GGUF服务[ERROR] -> 执行文件[%s]无法授权: %v", llmServerPath, err)
		return false
	}
	// 从配置中获取模型基础目录路径
	modelBaseDir := *config.ModelFilePath
	// 创建 embedding 模型子目录，若创建失败则记录错误日志并返回 false
	if err := os.MkdirAll(modelBaseDir+"/embedding", 0755); err != nil {
		log.Printf("GGUF服务[ERROR] -> 文本嵌入模型目录 创建失败: %v", err)

		return false
	}
	// 创建 reasoning 模型子目录，若创建失败则记录错误日志并返回 false
	if err := os.MkdirAll(modelBaseDir+"/reasoning", 0755); err != nil {
		log.Printf("GGUF服务[ERROR] -> 推理模型目录 创建失败: %v", err)
		return false
	}
	// 创建 visualing 模型子目录，若创建失败则记录错误日志并返回 false
	if err := os.MkdirAll(modelBaseDir+"/visualing", 0755); err != nil {
		log.Printf("GGUF服务[ERROR] -> 视觉模型目录 创建失败: %v", err)
		return false
	}
	// 所有初始化操作成功，返回 true
	return true
}

/**
 * @description: 为不同类型的模型启动对应的 GGUF 服务实例
 * @param {map[string]string} modelPaths 映射，键为模型类型，值为模型文件路径
 * @return {*}
 */
func startServersWithTypes(modelPaths map[string]string) {
	// 从配置中获取基础端口号
	basePort := *config.ModelUrlPort
	// 遍历所有模型类型和对应的模型路径
	for modelType, modelPath := range modelPaths {
		// 若模型路径为空，记录警告日志并跳过当前循环
		if modelPath == "" {
			log.Printf("GGUF服务[WARNING] -> 类型[%s]未找到模型", modelType)
			continue
		}
		// 增加 最大模型数量
		config.MaxModelAmount++
		// 启动单个模型服务
		startServerForModel(modelType, modelPath, basePort)
	}
}
